package core;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.WeekFields;
import java.util.Calendar;
import java.util.Date;

/**
 * 日期时间处理工具（空值安全优化版）
 * <p>
 * 所有方法均进行参数空值校验，避免NPE异常
 * 参考Hutool的DateUtil类实现，提供丰富的日期处理方法。
 * </p>
 */
public final class DateUtil {
    private static final DateTimeFormatter DEFAULT_FORMATTER =
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private DateUtil() {
        throw new AssertionError("Utility class");
    }

    /**
     * 当前时间，转换为{@link LocalDateTime}对象
     *
     * @return 当前时间
     */
    public static LocalDateTime date() {
        return LocalDateTime.now();
    }

    /**
     * {@link Date}类型时间转为{@link LocalDateTime}
     *
     * @param date Date对象，如果传入{@code null}，返回{@code null}
     * @return {@link LocalDateTime}对象
     */
    public static LocalDateTime date(Date date) {
        if (date == null) {
            return null;
        }
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * {@link Calendar}类型时间转为{@link LocalDateTime}
     *
     * @param calendar {@link Calendar}，如果传入{@code null}，返回{@code null}
     * @return {@link LocalDateTime}对象
     */
    public static LocalDateTime date(Calendar calendar) {
        if (calendar == null) {
            return null;
        }
        return calendar.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * {@link TemporalAccessor}类型时间转为{@link LocalDateTime}
     *
     * @param temporalAccessor {@link TemporalAccessor},常用子类： {@link LocalDateTime}、 LocalDate，如果传入{@code null}，返回{@code null}
     * @return {@link LocalDateTime}对象
     */
    public static LocalDateTime date(TemporalAccessor temporalAccessor) {
        if (temporalAccessor == null) {
            return null;
        }
        return LocalDateTime.from(temporalAccessor);
    }

    /**
     * 获取当前时间戳（毫秒）
     *
     * @return 当前时间戳
     */
    public static long currentMillis() {
        return System.currentTimeMillis();
    }

    /**
     * 获取当前时间戳（秒）
     *
     * @return 当前时间秒数
     */
    public static long currentSeconds() {
        return System.currentTimeMillis() / 1000;
    }

    /**
     * 当前时间，格式 yyyy-MM-dd HH:mm:ss
     *
     * @return 当前时间的标准形式字符串
     */
    public static String now() {
        return format(date());
    }

    /**
     * 当前日期，格式 yyyy-MM-dd
     *
     * @return 当前日期的标准形式字符串
     */
    public static String today() {
        return format(LocalDate.now());
    }

    /**
     * 获得年的部分
     *
     * @param date 日期
     * @return 年的部分
     */
    public static int year(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getYear();
    }

    /**
     * 获得月份，从1开始计数
     *
     * @param date 日期
     * @return 月份，从1开始计数
     */
    public static int month(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getMonthValue();
    }

    /**
     * 获得指定日期是所在年份的第几周
     *
     * @param date 日期
     * @return 周
     */
    public static int weekOfYear(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.get(WeekFields.ISO.weekOfYear());
    }

    /**
     * 获得指定日期是这个日期所在月份的第几天
     *
     * @param date 日期
     * @return 天
     */
    public static int dayOfMonth(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getDayOfMonth();
    }

    /**
     * 获得指定日期是这个日期所在年的第几天
     *
     * @param date 日期
     * @return 天
     */
    public static int dayOfYear(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getDayOfYear();
    }

    /**
     * 获得指定日期是星期几，1表示周一，7表示周日
     *
     * @param date 日期
     * @return 天
     */
    public static int dayOfWeek(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getDayOfWeek().getValue();
    }

    /**
     * 获得指定日期的小时数部分
     *
     * @param date 日期
     * @return 小时数
     */
    public static int hour(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getHour();
    }

    /**
     * 获得指定日期的分钟数部分
     *
     * @param date 日期
     * @return 分钟数
     */
    public static int minute(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getMinute();
    }

    /**
     * 获得指定日期的秒数部分
     *
     * @param date 日期
     * @return 秒数
     */
    public static int second(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getSecond();
    }

    /**
     * 获得指定日期的毫秒数部分
     *
     * @param date 日期
     * @return 毫秒数
     */
    public static int millisecond(LocalDateTime date) {
        if (date == null) {
            return 0;
        }
        return date.getNano() / 1_000_000;
    }

    /**
     * 日期格式化（默认格式）
     *
     * @param dateTime 待格式化时间
     * @return 格式化后的字符串（输入空时返回空字符串）
     */
    public static String format(LocalDateTime dateTime) {
        return format(dateTime, DEFAULT_FORMATTER);
    }

    /**
     * 日期格式化（默认格式）
     *
     * @param date 待格式化日期
     * @return 格式化后的字符串（输入空时返回空字符串）
     */
    public static String format(LocalDate date) {
        if (date == null) {
            return "";
        }
        return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    /**
     * 自定义格式日期格式化
     *
     * @param dateTime 待格式化时间
     * @param pattern  日期格式
     * @return 格式化后的字符串（输入空时返回空字符串）
     */
    public static String format(LocalDateTime dateTime, String pattern) {
        if (dateTime == null || StringUtil.isBlank(pattern)) return "";
        return format(dateTime, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期格式化（Formatter版）
     *
     * @param dateTime  待格式化时间
     * @param formatter 格式化器
     * @return 格式化后的字符串
     */
    private static String format(LocalDateTime dateTime, DateTimeFormatter formatter) {
        if (dateTime == null || formatter == null) return "";
        return dateTime.format(formatter);
    }

    /**
     * 解析日期字符串（默认格式）
     *
     * @param dateStr 日期字符串
     * @return 解析后的时间（解析失败返回null）
     */
    public static LocalDateTime parse(String dateStr) {
        return parse(dateStr, DEFAULT_FORMATTER);
    }

    /**
     * 自定义格式解析日期
     *
     * @param dateStr 日期字符串
     * @param pattern 日期格式
     * @return 解析后的时间（输入空或解析失败返回null）
     */
    public static LocalDateTime parse(String dateStr, String pattern) {
        if (StringUtil.isBlank(dateStr) || StringUtil.isBlank(pattern)) return null;
        return parse(dateStr, DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * 日期解析（Formatter版）
     *
     * @param dateStr   日期字符串
     * @param formatter 格式化器
     * @return 解析后的时间
     */
    private static LocalDateTime parse(String dateStr, DateTimeFormatter formatter) {
        if (StringUtil.isBlank(dateStr) || formatter == null) return null;
        try {
            return LocalDateTime.parse(dateStr, formatter);
        } catch (DateTimeParseException e) {
            return null;
        }
    }

    /**
     * Date转LocalDateTime（空值安全）
     *
     * @param date 旧日期对象
     * @return 转换后的时间（输入空时返回null）
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        if (date == null) return null;
        return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * LocalDateTime转Date
     *
     * @param localDateTime LocalDateTime对象
     * @return Date对象
     */
    public static Date toDate(LocalDateTime localDateTime) {
        if (localDateTime == null) return null;
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * String转LocalDateTime
     *
     * @param dateStr 日期字符串
     * @param pattern 日期格式
     * @return LocalDateTime对象
     */
    public static LocalDateTime toLocalDateTime(String dateStr, String pattern) {
        if (StringUtil.isBlank(dateStr) || StringUtil.isBlank(pattern)) return null;
        return parse(dateStr, pattern);
    }

    /**
     * LocalDateTime转String
     *
     * @param localDateTime LocalDateTime对象
     * @param pattern       日期格式
     * @return 日期字符串
     */
    public static String toString(LocalDateTime localDateTime, String pattern) {
        if (localDateTime == null || StringUtil.isBlank(pattern)) return "";
        return format(localDateTime, pattern);
    }

    /**
     * Date转String
     *
     * @param date    Date对象
     * @param pattern 日期格式
     * @return 日期字符串
     */
    public static String toString(Date date, String pattern) {
        if (date == null || StringUtil.isBlank(pattern)) return "";
        return format(toLocalDateTime(date), pattern);
    }

    /**
     * String转Date
     *
     * @param dateStr 日期字符串
     * @param pattern 日期格式
     * @return Date对象
     */
    public static Date toDate(String dateStr, String pattern) {
        if (StringUtil.isBlank(dateStr) || StringUtil.isBlank(pattern)) return null;
        LocalDateTime localDateTime = parse(dateStr, pattern);
        if (localDateTime == null) return null;
        return toDate(localDateTime);
    }

    /**
     * Calendar转LocalDateTime
     *
     * @param calendar Calendar对象
     * @return LocalDateTime对象
     */
    public static LocalDateTime toLocalDateTime(Calendar calendar) {
        if (calendar == null) return null;
        return calendar.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    /**
     * LocalDateTime转Calendar
     *
     * @param localDateTime LocalDateTime对象
     * @return Calendar对象
     */
    public static Calendar toCalendar(LocalDateTime localDateTime) {
        if (localDateTime == null) return null;
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(toDate(localDateTime));
        return calendar;
    }

    /**
     * String转Calendar
     *
     * @param dateStr 日期字符串
     * @param pattern 日期格式
     * @return Calendar对象
     */
    public static Calendar toCalendar(String dateStr, String pattern) {
        if (StringUtil.isBlank(dateStr) || StringUtil.isBlank(pattern)) return null;
        LocalDateTime localDateTime = parse(dateStr, pattern);
        if (localDateTime == null) return null;
        return toCalendar(localDateTime);
    }

    /**
     * Calendar转String
     *
     * @param calendar Calendar对象
     * @param pattern  日期格式
     * @return 日期字符串
     */
    public static String toString(Calendar calendar, String pattern) {
        if (calendar == null || StringUtil.isBlank(pattern)) return "";
        return format(toLocalDateTime(calendar), pattern);
    }

    /**
     * Date转Calendar
     *
     * @param date Date对象
     * @return Calendar对象
     */
    public static Calendar toCalendar(Date date) {
        if (date == null) return null;
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    /**
     * Calendar转Date
     *
     * @param calendar Calendar对象
     * @return Date对象
     */
    public static Date toDate(Calendar calendar) {
        if (calendar == null) return null;
        return calendar.getTime();
    }

    /**
     * 获取当前日期（系统时区）
     *
     * @return 当前日期
     */
    public static LocalDate todayDate() {
        return LocalDate.now();
    }

    /**
     * 获取当前日期时间（系统时区）
     *
     * @return 当前日期时间
     */
    public static LocalDateTime nowDateTime() {
        return LocalDateTime.now();
    }

    /**
     * 计算两个日期之间的时间差（秒）
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 时间差（秒）
     */
    public static long betweenSecond(LocalDateTime start, LocalDateTime end) {
        if (start == null || end == null) return 0;
        return Duration.between(start, end).getSeconds();
    }

    /**
     * 计算两个日期之间的时间差（分钟）
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 时间差（分钟）
     */
    public static long betweenMinute(LocalDateTime start, LocalDateTime end) {
        return betweenSecond(start, end) / 60;
    }

    /**
     * 计算两个日期之间的时间差（小时）
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 时间差（小时）
     */
    public static long betweenHour(LocalDateTime start, LocalDateTime end) {
        return betweenMinute(start, end) / 60;
    }

    /**
     * 计算两个日期之间的时间差（天）
     *
     * @param start 开始时间
     * @param end   结束时间
     * @return 时间差（天）
     */
    public static long betweenDay(LocalDateTime start, LocalDateTime end) {
        if (start == null || end == null) return 0;
        return Duration.between(start, end).toDays();
    }

    /**
     * 日期偏移（增加天数）
     *
     * @param date 日期
     * @param days 增加的天数
     * @return 偏移后的日期
     */
    public static LocalDateTime offset(LocalDateTime date, long days) {
        if (date == null) return null;
        return date.plusDays(days);
    }

    /**
     * 日期偏移（增加小时）
     *
     * @param date  日期
     * @param hours 增加的小时数
     * @return 偏移后的日期
     */
    public static LocalDateTime offsetHour(LocalDateTime date, long hours) {
        if (date == null) return null;
        return date.plusHours(hours);
    }

    /**
     * 日期偏移（增加分钟）
     *
     * @param date    日期
     * @param minutes 增加的分钟数
     * @return 偏移后的日期
     */
    public static LocalDateTime offsetMinute(LocalDateTime date, long minutes) {
        if (date == null) return null;
        return date.plusMinutes(minutes);
    }

    /**
     * 日期偏移（增加秒）
     *
     * @param date    日期
     * @param seconds 增加的秒数
     * @return 偏移后的日期
     */
    public static LocalDateTime offsetSecond(LocalDateTime date, long seconds) {
        if (date == null) return null;
        return date.plusSeconds(seconds);
    }

    /**
     * 判断是否为闰年
     *
     * @param date 日期
     * @return 是否为闰年
     */
    public static boolean isLeapYear(LocalDateTime date) {
        if (date == null) return false;
        return Year.isLeap(date.getYear());
    }

    /**
     * 判断是否为周末（周六或周日）
     *
     * @param date 判定的日期
     * @return 是否为周末（周六或周日）
     */
    public static boolean isWeekend(LocalDateTime date) {
        if (date == null) return false;
        int dayOfWeek = date.getDayOfWeek().getValue();
        return dayOfWeek == 6 || dayOfWeek == 7; // 6=Saturday, 7=Sunday
    }

    /**
     * 获取指定日期所在月的第一天
     *
     * @param date 日期
     * @return 月的第一天
     */
    public static LocalDateTime beginOfMonth(LocalDateTime date) {
        if (date == null) return null;
        return date.withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 获取指定日期所在月的最后一天
     *
     * @param date 日期
     * @return 月的最后一天
     */
    public static LocalDateTime endOfMonth(LocalDateTime date) {
        if (date == null) return null;
        return date.withDayOfMonth(date.toLocalDate().lengthOfMonth()).withHour(23).withMinute(59).withSecond(59).withNano(999_999_999);
    }

    /**
     * 获取指定日期所在年的第一天
     *
     * @param date 日期
     * @return 年的第一天
     */
    public static LocalDateTime beginOfYear(LocalDateTime date) {
        if (date == null) return null;
        return date.withDayOfYear(1).withHour(0).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 获取指定日期所在年的最后一天
     *
     * @param date 日期
     * @return 年的最后一天
     */
    public static LocalDateTime endOfYear(LocalDateTime date) {
        if (date == null) return null;
        return date.withDayOfYear(date.toLocalDate().lengthOfYear()).withHour(23).withMinute(59).withSecond(59).withNano(999_999_999);
    }

    /**
     * 获取指定日期所在周的第一天（周一）
     *
     * @param date 日期
     * @return 周的第一天
     */
    public static LocalDateTime beginOfWeek(LocalDateTime date) {
        if (date == null) return null;
        return date.minusDays(date.getDayOfWeek().getValue() - 1).withHour(0).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 获取指定日期所在周的最后一天（周日）
     *
     * @param date 日期
     * @return 周的最后一天
     */
    public static LocalDateTime endOfWeek(LocalDateTime date) {
        if (date == null) return null;
        return date.plusDays(7 - date.getDayOfWeek().getValue()).withHour(23).withMinute(59).withSecond(59).withNano(999_999_999);
    }

    /**
     * 获取指定日期所在天的开始时间（00:00:00）
     *
     * @param date 日期
     * @return 天的开始时间
     */
    public static LocalDateTime beginOfDay(LocalDateTime date) {
        if (date == null) return null;
        return date.withHour(0).withMinute(0).withSecond(0).withNano(0);
    }

    /**
     * 获取指定日期所在天的结束时间（23:59:59.999999999）
     *
     * @param date 日期
     * @return 天的结束时间
     */
    public static LocalDateTime endOfDay(LocalDateTime date) {
        if (date == null) return null;
        return date.withHour(23).withMinute(59).withSecond(59).withNano(999_999_999);
    }
}